#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include "protocal.hpp"
using namespace std;

class calclient
{
public:
    calclient(const string &ip, const uint16_t port)
        : _sockfd(-1), _serverip(ip), _serverport(port)
    {
    }
    void initclient()
    {
        // 创建套接字
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            cerr << "socket fail" << endl;
            exit(2);
        }
        // 客户端是需要bind,但是不需要我们显示的bind，OS会自动帮我们bind，会在合适的时机
        // 更不要监听工作，因为你是客服端，你是要连接服务器，其他的事不需要去做
    }
    void start()
    {
        // 既然要连接服务器，那么我们肯定是需要获得服务器的基本信息
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_addr.s_addr = inet_addr(_serverip.c_str());
        server.sin_family = AF_INET;
        server.sin_port = htons(_serverport);
        // 开始连接
        if (connect(_sockfd, (struct sockaddr *)&server, sizeof(server)) != 0)
        {
            // 连接失败，继续尝试连接
            cerr << "connect fail" << endl;
        }
        else
        {
            string message;
            string inbuffer;
            while (true)
            {
                cout << "Enter# " << endl;
                getline(cin, message);
                // 把输入的字符串转换成request结构
                Request req = Praseline(message);
                cout<<"输入的信息"<<req._x<<" "<<req._y<<" "<<req._op<<endl;
                // 序列化，然后再添加报文
                string req_str, req_text;
                req.serialize(&req_str);
                enLength(req_str, &req_text);
                cout<<"发送的报头："<<req_text<<endl;
                // 发送到服务器
                send(_sockfd, req_text.c_str(), req_text.size(), 0); // 这里后序可能有问题，后面再处理
                // 接受数据
                string resp_str, resp_text;
                if (!recvPackage(_sockfd, inbuffer, &resp_text))
                    return;
                // 去报文，反序列化，得到结果打印出来
                if (!deLength(resp_text, &resp_str))
                    return;
                Response resp;
                resp.deserialize(resp_str);
                std::cout << "exitCode: " << resp._exitcode << std::endl;
                std::cout << "result: " << resp._result << std::endl;
            }
        }
    }
    Request Praseline(const string &msg)
    {
        //"1+1"
        int status = 0; // 0:操作符之前， 1：操作符  2：操作符之后
        string x, y;
        char op;
        int i = 0;
        while (i < msg.size())
        {
            switch (status)
            {
            case 0:
            {
                if (!isdigit(msg[i]))
                {
                    op = msg[i];
                    status = 1;
                }
                else
                    x += msg[i++];
            }
            break;
            case 1:
            {
                ++i;
                status = 2;
            }
            break;
            case 2:
            {
                y += msg[i++];
            }
            break;
            }
        }
        return Request(stoi(x), stoi(y), op);
    }
    ~calclient()
    {
        // 这里可以关闭文件描述符，也可以不关闭，因为文件描述符随进程，进程退出OS必定回收
        close(_sockfd);
    }

private:
    string _serverip;     // 服务器ip
    uint16_t _serverport; // 服务器端口号
    int _sockfd;          // 套接字
};